﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;

namespace KPIS.GERP.SERVICE.Model
{
    /// <summary>
    /// for keep all type of task that open via approval line, both active and finished task
    /// </summary>
    public class approval_task
    {
        public approval_task()
        {
            StackTrace stackTrace = new StackTrace(); 
            StackFrame[] stackFrames = stackTrace.GetFrames(); 

            // write call stack method names
            AddHistory("ApprovalTask is creating by non parametor constructor, come from below call stack");
            if(stackFrames != null)
                foreach (StackFrame stackFrame in stackFrames)
                {
                    AddHistory(stackFrame.GetMethod().Name);
                }
        }

        public approval_task(string pTitle, Guid pWfInstantId, int pRequestEmpSeq, int pCreatorEmpSeq)
        {
            StartWhen = DateTime.Now;
            Title = pTitle;
            WfInstantId = pWfInstantId;
            RequestEmpSeq = pRequestEmpSeq;
            Status = TaskStatus.Initializing;
            CreatedBy = pCreatorEmpSeq;
            CreatedWhen = DateTime.Now;
            CurrentStep = 0;
            ActionCount = 0;
            ActionDatas = new List<ActionData>();
            Ready = false;
        }

        [Key]
        public int ApprovalTaskSeq { get; set; }

        public Guid WfInstantId { get; set; }

        public string Title { get; set; }

        public DateTime StartWhen { get; set; }

        public DateTime? StopWhen { get; set; }

        public int RequestEmpSeq { get; set; }

        public int CurrentStep { get; set; }

        public bool Ready { get; set; }

        public int ActionCount { get; set; }

        public string ApproveAs { get; set; }

        public hrm_ps_employees RequestEmp { get; set; }

        public hrm_ps_employees CreatorEmp { get; set; }

        public string RecordStatus { get; set; }

        public int? CreatedBy { get; set; }

        public int? UpdatedBy { get; set; }
        
        public DateTime? CreatedWhen { get; set; }

        public DateTime? UpdatedWhen { get; set; }

        public TaskStatus Status { get; set; }

        [NotMapped]
        public string InitialAction { get; set; }

        public approval_reference Reference { get; set; }
        [NotMapped]
        public int RefSeq { get; set; }
        [NotMapped]
        public int RefTypeSeq { get; set; }
        [NotMapped]
        public List<rule_approve> RuleApproves { get; set; }
        [NotMapped]
        public List<TaskHistory> HistoriesCollection { get; set; }
        [NotMapped]
        public List<action_history> _actionHistories { get; set; }
        [NotMapped]
        public List<rule_approve_custom_button> custom_buttons { get; set; }
        [NotMapped]
        public List<ActionData> ActionDatas { get; set; }

        public bool Completed
        {
            get { return ApprovalUtil.IsComplete(Status); }
        }

        public int CurrentStepIndex
        {
            get { return CurrentStep - 1; }
        }

        public bool IsContainAction(string actionChar)
        {
            return Action.Contains(actionChar);
        }

        public List<string> ActionWord
        {
            get { return Action.Split(',').Select(ApprovalUtil.TranslateAction).ToList(); }
        }

        public string StatusWord
        {
            get { return ApprovalUtil.TranslateStatus(Status); }
        }

        public int CountRuleApprove
        {
            get { return RuleApproves == null ? 0 : RuleApproves.Count; }
        }

        public bool IsCompeteAtTheStep(int step)
        {
            return RuleApproves.Count > step;
        }

        public void Walk()
        {
            CurrentStep++;
        }

        public int CountAllStep
        {
            get { return RuleApproves != null ? RuleApproves.Count : 0; }
        }

        public ActionData LastActionData
        {
            get
            {
                try
                {
                    if(ActionDatas != null && ActionDatas.Count > 0)
                        return ActionDatas.Last();
                    else 
                         return  null ;
                } catch
                {
                    return null;
                }
            }
        }

        public string Action
        {
            get
            {
                if (CurrentRuleApprove == null) return string.Empty;
                return CurrentRuleApprove.action;
            }
            set { if (CurrentRuleApprove != null) CurrentRuleApprove.action = value; }
        }

        public int CountApproverEmployee
        {
            get
            {
                return CurrentRuleApprove == null || CurrentRuleApprove.Approvers == null
                           ? 0
                           : CurrentRuleApprove.Approvers.Count;
            }
        }

        public List<hrm_ps_employees> ApproverEmployees
        {
            get
            {
                return CurrentRuleApprove == null || CurrentRuleApprove.Approvers == null
                           ? new List<hrm_ps_employees>()
                           : CurrentRuleApprove.Approvers;
            }
            set
            {
                if (CurrentRuleApprove == null) return;
                CurrentRuleApprove.Approvers = value;
            }
        }


        public bool IsApproverTypeUpperPosition
        {
            get
            {
                if (NextRuleApprove != null && NextRuleApprove.approver_type == "1") return true;
                else return false;
            }
        }

        public bool IsApproverTypeHeadJobSite
        {
            get
            {
                if (NextRuleApprove != null && NextRuleApprove.approver_type == "5") return true;
                else return false;
            }
        }

        [NotMapped]
        public List<rule_approve> GetRuleApproveType1AfterCurrent
        {
            get
            {
                var ruleApproveAfterCurrent = new List<rule_approve>();
                if (RuleApproves == null) RuleApproves = new List<rule_approve>();
                int i = 0;

                var ruleApproveMoreThanCurrentStep = RuleApproves.Where(o => o != null && o.step > CurrentStep) ??
                                                     new List<rule_approve>();
                var ruleApproveOrdered = ruleApproveMoreThanCurrentStep.OrderBy(o => o.step);
                foreach (var ruleApprove in ruleApproveOrdered)
                {
                    if (ruleApprove.approver_type == "1")
                        ruleApproveAfterCurrent.Add(ruleApprove);
                    else
                        break;
                }
                return ruleApproveAfterCurrent;
            }
        }


        [NotMapped]
        public rule_approve NextRuleApprove
        {
            get
            {
                int nextStepIndex = CurrentStepIndex + 1;
                return RuleApproves != null &&
                       RuleApproves.Count > nextStepIndex &&
                       nextStepIndex > 0
                           ? RuleApproves[nextStepIndex]
                           : null;
            }
            set
            {
                int nextStepIndex = CurrentStepIndex + 1;
                if (RuleApproves != null && RuleApproves.Count > nextStepIndex && nextStepIndex > 0)
                    RuleApproves[nextStepIndex] = value;
            }
        }

        [NotMapped]
        public string CurrentRuleApproveTypeName
        {
            get
            {
                return CurrentRuleApprove == null
                       || CurrentRuleApprove.rule_approve_type == null
                           ? string.Empty
                           : CurrentRuleApprove.rule_approve_type.rule_approve_type_name;
            }
        }

        [NotMapped]
        public string FirstRuleApproveTypeName
        {
            get
            {
                return RuleApproves == null || RuleApproves.Count == 0 ||
                    RuleApproves[0] == null || RuleApproves[0].rule_approve_type == null
                           ? string.Empty
                           : RuleApproves[0].rule_approve_type.rule_approve_type_name;
            }
        }

        [NotMapped]
        public string CurrentRuleApproveApproverName
        {
            get
            {
                return CurrentRuleApprove == null
                       || CurrentRuleApprove.approver_type_name == null
                           ? string.Empty
                           : CurrentRuleApprove.approver_type_name;
            }
        }

        [NotMapped]
        public rule_approve CurrentRuleApprove
        {
            get
            {
                return RuleApproves != null &&
                       RuleApproves.Count > CurrentStepIndex &&
                       CurrentStepIndex >= 0
                           ? RuleApproves[CurrentStepIndex]
                           : null;
            }
            set
            {
                if (RuleApproves != null && RuleApproves.Count > CurrentStepIndex && CurrentStepIndex >= 0)
                    RuleApproves[CurrentStepIndex] = value;
            }
        }

        #region Misc Methods

        public string RenderCustomButtons
        {
            get
            {
                if (custom_buttons == null) return "";
                return string.Join("<br />", custom_buttons.Select(c =>
                                                                   string.Format("<a href='{0}'>{1}</a>", c.navigate_url,
                                                                                 c.rule_approve_custom_name)));
            }
        }

        public void AddHistoryHead(string pMessage, string pRemarks = "", bool isForUser = false)
        {
            if (HistoriesCollection == null) HistoriesCollection = new List<TaskHistory>();
            HistoriesCollection.Add(new TaskHistory
                {
                    Message = pMessage,
                    Remarks = pRemarks,
                    When = DateTime.Now,
                    IsHead = true,
                    IsShowToUser = isForUser
                });
        }

        public void AddHistory(string pMessage, string pRemarks = "", bool isForUser = false)
        {
            if (HistoriesCollection == null) HistoriesCollection = new List<TaskHistory>();
            HistoriesCollection.Add(new TaskHistory
                {
                    Message = pMessage,
                    Remarks = pRemarks,
                    When = DateTime.Now,
                    IsHead = false,
                    IsShowToUser = isForUser
                });
        }

        public string AllHistory()//bool getTechnical = true)
        {
            var firstOrDefault = HistoriesCollection.FirstOrDefault();
            if (firstOrDefault != null && (firstOrDefault.Message ?? string.Empty).StartsWith("ApprovalTask is creating")) return "";
            var histories = new StringBuilder();
            var intHeadCount = 1;
            var intItemCount = 1;
            foreach (TaskHistory eachHistory in HistoriesCollection)
            {
                //if (eachHistory.IsShowToUser || getTechnical)
                //{
                    if (!eachHistory.IsHead)
                        histories.Append(string.Format(@"&nbsp;&nbsp;&nbsp;&nbsp;<span style='color:silver'>เมื่อ {1}</span> - {2} {3}{4}",
                                                       intHeadCount++,
                                                       eachHistory.When.ToString("d MMM yyyy HH:mm:ss"),
                                                       (eachHistory.Message.StartsWith("-")
                                                            ? "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&bull;" +
                                                              eachHistory.Message.Substring(1, eachHistory.Message.Length - 1)
                                                            : eachHistory.Message),
                                                       (string.IsNullOrWhiteSpace(eachHistory.Remarks)
                                                            ? string.Empty
                                                            : " หมายเหตุ:" + eachHistory.Remarks),
                                                       Environment.NewLine));
                    else
                        histories.Append(
                            string.Format(@"<br/>&nbsp;&nbsp;&nbsp;&nbsp;<span style='color:silver'>เมื่อ {1}</span> -<b> {2} {3}{4}</b>",
                                          intItemCount++,
                                          eachHistory.When.ToString("d MMM yyyy HH:mm:ss"),
                                          (eachHistory.Message.StartsWith("-")
                                               ? "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&bull;" +
                                                 eachHistory.Message.Substring(1, eachHistory.Message.Length - 1)
                                               : eachHistory.Message) ,
                                          (string.IsNullOrWhiteSpace(eachHistory.Remarks)
                                               ? string.Empty : " หมายเหตุ:" + eachHistory.Remarks),
                                          Environment.NewLine));
                //}
            }
            return histories.ToString();
        }
        
        public bool TakeAction(TaskAction pTaskAction, hrm_ps_employees emp, string messageTitle = "", string messageReasons = "")
        {
            if (ActionDatas == null) ActionDatas = new List<ActionData>();
            int nextStep = CurrentStep + 1;
            if(ApprovalUtil.IsComplete(pTaskAction))
            {
                nextStep = RuleApproves == null ? 1 : RuleApproves.Count;
            }
            ActionDatas.Add(new ActionData
                {
                    By = emp,
                    On = DateTime.Now,
                    OnStep = nextStep,
                    Title = messageTitle,
                    Reasons = messageReasons,
                    RuleApprove = CurrentRuleApprove
                });

            if (CurrentStep == 0)
                AddHistory("งานอนุมัติกำลังเข้าสู่ขั้นตอนที่ 1 ");
            else if(NextRuleApprove != null)
            {
                AddHistory(string.Format("ส่งคำขออนุมัติไปยัง {0} โดยมีสิทธิ์ในการ <span style='color:blue'>{1}</span> สำหรับขั้นตอนที่ {2} ",
                                  emp.emp_name, string.Join(",", ActionWord), nextStep));

                messageTitle = string.IsNullOrWhiteSpace(messageTitle) ? string.Empty : "หัวข้อการส่งต่อ" + messageTitle;
                messageReasons = string.IsNullOrWhiteSpace(messageReasons) ? string.Empty : "ให้เหตุผลแนบท้ายว่า" + messageReasons;

                if (messageTitle + messageReasons != string.Empty)
                    AddHistory(string.Format("{0} {1}", messageTitle, messageReasons));
            } 

            //not equal to currentruleapprove
            if (NextRuleApprove != null)
            {
                NextRuleApprove.action_when = DateTime.Now;
                NextRuleApprove.action_by = emp.emp_seq;
                NextRuleApprove.action_by_pos_seq = emp.pos_seq;
            }

            Status = ApprovalUtil.StatusAfterTakeAction(pTaskAction);
            UpdatedWhen = DateTime.Now;
            UpdatedBy = emp.emp_seq;
            ActionCount++;

            return ApprovalUtil.IsComplete(Status);
        }

        public string RefTableStatus
        {
            get
            {
                switch (Status)
                {
                    case TaskStatus.Failed:
                        return "C";
                    case TaskStatus.CancelStatus:
                        return "C";
                    case TaskStatus.Approved:
                        return "A";
                    case TaskStatus.Finished:
                        return null;
                    case TaskStatus.Initializing:
                        return null;
                    case TaskStatus.Rejected:
                        return "J";
                    case TaskStatus.WaitStatus:
                        return "P";
                    default:
                        return null;
                }
            }
        }

        #endregion
    }

    public static class ApprovalStorage
    {
        public static Dictionary<Guid, approval_task> Tasks = new Dictionary<Guid, approval_task>();
    }

    public enum TaskStatus
    {
        /// <summary>
        /// Staget at first loading needed data
        /// </summary>
        Initializing,
        /// <summary>
        /// Pending Status, waiting approver to take action approve or reject
        /// </summary>
        PendingStatus,
        /// <summary>
        /// Pending Status, waiting approver to take action approve or reject
        /// </summary>
        WaitStatus,
        /// <summary>
        /// Cancel by user
        /// </summary>
        CancelStatus,
        /// <summary>
        /// Approve by approver
        /// </summary>
        Approved,
        /// <summary>
        /// Reject by approver
        /// </summary>
        Rejected,
        /// <summary>
        /// If last status is read only (R), the flow just send message to inbox but unable to action
        /// </summary>
        Finished,
        /// <summary>
        /// Load failed, some data needed but unable to get
        /// </summary>
        Failed
    }

    public enum TaskAction
    {
        F, // Forworder
        A, // Approver
        J, // Rejectable
        R, // View only (Read - only)
        N, // Forward back (Need more detail)
        C, // Cancel
    }

    public class TaskHistory
    {
        public DateTime When { get; set; }
        public string Message { get; set; }
        public string Remarks { get; set; }
        public bool IsHead { get; set; }
        public bool IsShowToUser { get; set; }
    }

    public class ActionData
    {
        public int OnStep { get; set; }
        public hrm_ps_employees By { get; set; }
        public DateTime On { get; set; }
        public string Title { get; set; }
        public string Reasons { get; set; }
        [NotMapped]
        public rule_approve RuleApprove { get; set; }
    }

    public class ApprovalUtil
    {

        public static List<TaskStatus> CompletableStatuses = new List<TaskStatus>
            {
                TaskStatus.Approved,
                TaskStatus.Rejected,
                TaskStatus.Finished,
                TaskStatus.CancelStatus
            };
        public static List<TaskAction> CompletableActions = new List<TaskAction>
            {
                TaskAction.A,
                TaskAction.C,
                TaskAction.J
            };

        public static TaskStatus StatusAfterTakeAction(TaskAction pTaskAction)
        {
            TaskStatus result;
            switch (pTaskAction)
            {
                case TaskAction.A:
                    result = TaskStatus.Approved;
                    break;
                case TaskAction.F:
                    result = TaskStatus.PendingStatus;
                    break;
                case TaskAction.J:
                    result = TaskStatus.Rejected;
                    break;
                case TaskAction.N:
                    result = TaskStatus.PendingStatus;
                    break;
                case TaskAction.C:
                    result = TaskStatus.CancelStatus;
                    break;
                case TaskAction.R:
                    throw new Exception("ไม่สามารถกระทำการใดๆ จากคำสั่งที่เป็นการอ่านอย่างเดียวได้ (Read-only)");
                default:
                    throw new Exception("ไม่ทราบคำสั่งนี้");
            }
            return result;
        }

        public static string ConvertShortActionToWord(string shortActions = "")
        {
            string[] shortActionArr = shortActions.Split(',');
            return shortActionArr.Aggregate("", (current, s) => current + (TranslateAction(s) + " "));
        }

        public static string TranslateAction(string actionChar)
        {
            switch (actionChar)
            {
                case "A":
                    return "อนุมัติ";
                case "F":
                    return "ส่งต่อ";
                case "J":
                    return "ไม่อนุมัติ";
                case "N":
                    return "ส่งกลับ";
                case "C":
                    return "ยกเลิก";
                case "R":
                    return "";
                default:
                    return "";
            }
        }


        public static string TranslateStatus(TaskStatus statusEnumWord)
        {
            switch (statusEnumWord)
            {
                case TaskStatus.Initializing:
                    return "เริ่มต้นการทำงาน";
                case TaskStatus.PendingStatus:
                    return "รออนุมัติ";
                case TaskStatus.WaitStatus:
                    return "รอการยืนยันจากผู้ขอ";
                case TaskStatus.Approved:
                    return "ได้รับอนุมัติ";
                case TaskStatus.Rejected:
                    return "ไม่อนุมัติ";
                case TaskStatus.Finished:
                    return "งานอนุมัติสิ้นสุดลง";
                case TaskStatus.Failed:
                    return "ล้มเหลว อาจเกิดจากข้อมูลการทำงานไม่ถูกต้อง";
                case TaskStatus.CancelStatus:
                    return "ถูกยกเลิก";
                default:
                    return "";
            }
        }

        public static List<string> CompletableStatusesString
        {
            get { return CompletableStatuses.ConvertAll(x => x.ToString()); }
        }

        public static List<string> CompletableActionsString
        {
            get { return CompletableActions.ConvertAll(x => x.ToString()); }
        }

        public static bool IsComplete(TaskStatus pTaskStatus)
        {
            return CompletableStatuses.Contains(pTaskStatus);
        }

        public static bool IsComplete(TaskAction pTaskAction)
        {
            return CompletableActions.Contains(pTaskAction);
        }

        public static bool IsComplete(string pTaskStatus)
        {
            return CompletableStatusesString.Contains(pTaskStatus);
        }

        public static TaskStatus TaskStatusFromString(string pTaskStatus)
        {
            return (TaskStatus) Enum.Parse(typeof (TaskStatus), pTaskStatus);
        }

        public static TaskAction TaskActionFromString(string pTaskAction)
        {
            return (TaskAction) Enum.Parse(typeof (TaskAction), pTaskAction);
        }
    }
}